package signatureFiles;

import hashfunctions.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.BitSet;
import java.util.LinkedList;
import java.util.List;

import FilesManagers.FileManager;

import register.Register;
import register.RegisterID;

public class SignatureFileObject {
	private SignatureFileFile file;
	private int recordCount;
	private String path;
	private int nbits;
	//private BitSet hashAcumulator;
	private List<BitSet> hashAcumulators;
	private int numberOfHash;
	
	private void initializeAcumulators(int cant,int nbits){
		hashAcumulators = new LinkedList<BitSet>();
		for(int i=0; i<cant;i++){
			hashAcumulators.add(new BitSet(nbits));
			
		}
		
	}
	
	public SignatureFileObject(String path, int nbits, int numberOfHash) throws IOException{
		 
		this.path=path;
		this.nbits=nbits;
		initializeAcumulators(numberOfHash,nbits);
		recordCount=0;
		this.numberOfHash=numberOfHash;
		try {
			
			file=new SignatureFileFile(path,"r");
			file.close();
		} catch (FileNotFoundException e) {
        	//si el archivo no existe es un nuevo SignatureFile
        	file=new SignatureFileFile(path,"rw");
        	write();
        	file.close();
        	return; //se creo el archivo nuevo
        }
        //si llega aca es porque el archivo existe y hay que cargarlo
        load();
		        
	}
	
	public SignatureFileObject() throws IOException{
		this("signature.txt",128,1);
	}
	
	
	
	private void write() throws IOException {
		file.seek(0);
		file.writeBytes(path + "\n");
		file.writeBytes(Integer.toString(nbits) + "\n");
		file.writeBytes(Integer.toString(numberOfHash) + "\n");
		String dummy="          ";
		dummy=dummy.substring(Integer.toString(recordCount).length());
		file.writeBytes(Integer.toString(recordCount)+""+dummy+"\n");
		file.close();
	}

	private void load() throws IOException{
		if(file!=null) file.close();
		file=new SignatureFileFile(path,"rw");
		file.seek(0);
		path=file.readLine();		
		nbits=Integer.valueOf(file.readLine());
		numberOfHash = Integer.valueOf(file.readLine());
		recordCount=Integer.valueOf(file.readLine().trim());		
		
	}

	public void addToHash(String palabra) {
		BitSet aOrear;
		for(int i=0; i<numberOfHash;i++){
			aOrear=hash(palabra,i);
			hashAcumulators.get(i).or(aOrear);			
		}
	}

	
	public BitSet hash(String palabra, int num){
		BitSet salida=null;
		HashFunction_128 f;
		
		switch (num){
			case 0: f=new HashSHA1_128();	
				
					break;
			case 1: 
					f=new HashBobNervio_128();
					break;
			case 2: 				
					f=new HashMD5BN_128();
					break;
			case 3: 
					return hashManual(palabra);
					
			case 4: 				
					f=new HashSHA1BN_128();
					break;		
			case 5: 
					f=new HashSHA512_128();
					break;
			case 6: 
					f=new HashMD5_128();
					break;
			
					
			default: 
					f=new HashMD5_128();
		}
		try {
			//System.out.println("nbits = " +nbits);
			salida=HashExtender.hash(f, nbits, palabra);
			//System.out.println("Bits de la salida del hash extendido = " + salida.size());
		} catch (NoSuchAlgorithmException e) {
			// TODO Bloque catch generado automáticamente
			e.printStackTrace();
		}
		//return hashManual(palabra); //opcion de control
		return salida;
		
		
	}

	private BitSet hashManual(String palabra) {
		BitSet output=new BitSet(nbits);
		output.set(Math.abs(palabra.hashCode()*palabra.hashCode()-palabra.length())%nbits);
		output.set((palabra.length()*palabra.length()*palabra.length()*palabra.length())%nbits);
		output.set(Math.abs((palabra.hashCode())%nbits));
		output.set((int)Math.pow(palabra.charAt(0),palabra.length())%nbits);
		
		return output;
	}

	public void writeHash() throws IOException {
		load();
		String salida;
		file.seek(file.length());
		for(int j=0; j<numberOfHash;j++){
				//System.out.println(hashAcumulators.get(j).size());
			salida = Hash.bitSetToString(hashAcumulators.get(j));
			recordCount++;
			file.writeBytes(salida);
				//System.out.println(salida.length());
			hashAcumulators.get(j).clear();
		}
		
		write();
	}


	public void readNextHash() throws IOException{
		int largo;
		largo=nbits/8; //siempre va a ser multiplo de 8
		char caracter;
		this.initializeAcumulators(numberOfHash, nbits);
		for(int h=0; h<this.numberOfHash;h++){
			for(int i=0;i<largo;i++){
				caracter=(char)file.read/*UnsignedByte*/(); //no se si esto esta bien
					for(int j=i*8+7,k=128;k>0;j--,k/=2){
						if(caracter-k>=0){
							caracter-=k;
							hashAcumulators.get(h).set(j);
						}
							
					}
			}
					
		}
			
		
	}

	public Register search(String palabra) throws IOException{
		Register salida=new Register(new RegisterID(palabra));
		
		BitSet aComparar;//=hash(palabra);
		boolean encontrado=false;
		
		for(int i=1;i<=recordCount/numberOfHash;i++){
			readNextHash();
			for(int j=0;j<numberOfHash;j++){
				aComparar=hash(palabra,j);				
				hashAcumulators.get(j).and(aComparar);
				if(hashAcumulators.get(j).equals(aComparar)/* && estabaEnAnterior*/){
					encontrado=true;
					
				}else{
					encontrado=false;
					break;					
				}
				
			}
			
			if(encontrado){
				salida.addAtribute(i);encontrado=false;
			}
		}

		file.close();
		return salida;
		
		
	}

	public int getNumberOfHash() {
		return numberOfHash;
	}

	public void setNumberOfHash(int numberOfHash) {
		this.numberOfHash = numberOfHash;
		initializeAcumulators(this.numberOfHash,nbits);
	}
	
	
}
